/*     */ package com.zimbra.cs.service;
/*     */ 
/*     */ import com.google.common.annotations.VisibleForTesting;
/*     */ import com.google.common.base.Strings;
/*     */ import com.google.common.io.Closeables;
/*     */ import com.zimbra.common.calendar.ZCalendar.ZCalendarBuilder;
/*     */ import com.zimbra.common.calendar.ZCalendar.ZVCalendar;
/*     */ import com.zimbra.common.httpclient.HttpClientUtil;
/*     */ import com.zimbra.common.mime.ContentType;
/*     */ import com.zimbra.common.mime.shim.JavaMailInternetAddress;
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.service.ServiceException.Argument;
/*     */ import com.zimbra.common.soap.Element;
/*     */ import com.zimbra.common.util.FileUtil;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.ZimbraHttpConnectionManager;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.common.zmime.ZMimeBodyPart;
/*     */ import com.zimbra.common.zmime.ZMimeMultipart;
/*     */ import com.zimbra.cs.account.Account;
/*     */ import com.zimbra.cs.httpclient.HttpProxyUtil;
/*     */ import com.zimbra.cs.ldap.LdapUtil;
/*     */ import com.zimbra.cs.mailbox.Folder.SyncData;
/*     */ import com.zimbra.cs.mailbox.calendar.Invite;
/*     */ import com.zimbra.cs.mime.Mime.FixedMimeMessage;
/*     */ import com.zimbra.cs.mime.ParsedMessage;
/*     */ import com.zimbra.cs.util.BuildInfo;
/*     */ import com.zimbra.cs.util.JMSession;
/*     */ import com.zimbra.cs.util.Zimbra;
/*     */ import java.io.BufferedInputStream;
/*     */ import java.io.IOException;
/*     */ import java.io.StringReader;
/*     */ import java.io.UnsupportedEncodingException;
/*     */ import java.net.URLDecoder;
/*     */ import java.util.ArrayList;
/*     */ import java.util.Date;
/*     */ import java.util.Iterator;
/*     */ import java.util.List;
/*     */ import javax.mail.BodyPart;
/*     */ import javax.mail.MessagingException;
/*     */ import javax.mail.internet.InternetAddress;
/*     */ import javax.mail.internet.MimeBodyPart;
/*     */ import javax.mail.internet.MimeMessage;
/*     */ import javax.mail.internet.MimeMultipart;
/*     */ import javax.mail.internet.MimePart;
/*     */ import javax.mail.internet.ParseException;
/*     */ import org.apache.commons.httpclient.Header;
/*     */ import org.apache.commons.httpclient.HttpClient;
/*     */ import org.apache.commons.httpclient.HttpException;
/*     */ import org.apache.commons.httpclient.HttpState;
/*     */ import org.apache.commons.httpclient.HttpURL;
/*     */ import org.apache.commons.httpclient.HttpsURL;
/*     */ import org.apache.commons.httpclient.StatusLine;
/*     */ import org.apache.commons.httpclient.UsernamePasswordCredentials;
/*     */ import org.apache.commons.httpclient.auth.AuthScope;
/*     */ import org.apache.commons.httpclient.methods.GetMethod;
/*     */ import org.apache.commons.httpclient.params.HttpClientParams;
/*     */ import org.apache.commons.httpclient.params.HttpMethodParams;
/*     */ import org.apache.commons.httpclient.util.DateParseException;
/*     */ import org.cyberneko.html.parsers.SAXParser;
/*     */ import org.xml.sax.Attributes;
/*     */ import org.xml.sax.ContentHandler;
/*     */ import org.xml.sax.InputSource;
/*     */ import org.xml.sax.XMLReader;
/*     */ import org.xml.sax.helpers.DefaultHandler;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public class FeedManager
/*     */ {
/*     */   public static final int MAX_REDIRECTS = 3;
/*     */   
/*     */   public static final class SubscriptionData<T>
/*     */   {
/*     */     private final List<T> items;
/*     */     private String lastGuid;
/*     */     private long lastDate;
/*     */     private boolean notModified;
/*     */     
/*     */     static SubscriptionData<Object> NOT_MODIFIED()
/*     */     {
/*  91 */       return new SubscriptionData(new ArrayList(0), 0L, true);
/*     */     }
/*     */     
/*     */     SubscriptionData() {
/*  95 */       this(new ArrayList(), 0L);
/*     */     }
/*     */     
/*     */     SubscriptionData(List<T> items, long ldate) {
/*  99 */       this(items, ldate, false);
/*     */     }
/*     */     
/*     */     SubscriptionData(List<T> items, long lastModifiedDate, boolean notModified) {
/* 103 */       this.items = items;
/* 104 */       this.lastDate = lastModifiedDate;
/* 105 */       this.notModified = notModified;
/*     */     }
/*     */     
/*     */     void recordItem(T item, String guid, long date) {
/* 109 */       this.items.add(item);
/* 110 */       if (date > this.lastDate) {
/* 111 */         this.lastGuid = guid;this.lastDate = date;
/*     */       }
/*     */     }
/*     */     
/*     */     void recordFeedModifiedDate(long feedModified) {
/* 116 */       if (feedModified > this.lastDate) {
/* 117 */         this.lastDate = feedModified;
/*     */       }
/*     */     }
/*     */     
/*     */     public List<T> getItems() {
/* 122 */       return this.items;
/*     */     }
/*     */     
/*     */     public String getMostRecentGuid()
/*     */     {
/* 127 */       return this.lastGuid;
/*     */     }
/*     */     
/*     */ 
/*     */     public long getLastModifiedDate()
/*     */     {
/* 133 */       return this.lastDate;
/*     */     }
/*     */     
/*     */     public boolean isNotModified()
/*     */     {
/* 138 */       return this.notModified;
/*     */     }
/*     */   }
/*     */   
/*     */   private static String getBrowserTag() {
/* 143 */     String tag = " Zimbra/" + BuildInfo.MAJORVERSION + "." + BuildInfo.MINORVERSION + "." + BuildInfo.MICROVERSION;
/* 144 */     return tag.indexOf("unknown") == -1 ? tag : " Zimbra/8.0";
/*     */   }
/*     */   
/*     */ 
/*     */ 
/* 149 */   public static final String BROWSER_TAG = getBrowserTag();
/* 150 */   public static final String HTTP_USER_AGENT = "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.1.7)" + BROWSER_TAG;
/*     */   public static final String HTTP_ACCEPT = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*";
/*     */   private static final String HTML_HEADER = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML><HEAD><META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=utf-8\"></HEAD><BODY>";
/*     */   private static final String HTML_FOOTER = "</BODY></HTML>";
/*     */   
/*     */   public static class RemoteDataInfo { public final int statusCode;
/*     */     public final int redirects;
/*     */     public BufferedInputStream content;
/*     */     public final String expectedCharset;
/*     */     public final long lastModified;
/* 160 */     private GetMethod getMethod = null;
/*     */     
/*     */     public RemoteDataInfo(int statusCode, int redirects, BufferedInputStream content, String expectedCharset, long lastModified)
/*     */     {
/* 164 */       this.statusCode = statusCode;
/* 165 */       this.redirects = redirects;
/* 166 */       this.content = content;
/* 167 */       this.expectedCharset = expectedCharset;
/* 168 */       this.lastModified = lastModified;
/*     */     }
/*     */     
/* 171 */     public GetMethod getGetMethod() { return this.getMethod; }
/*     */     
/*     */ 
/* 174 */     public void setGetMethod(GetMethod getMethod) { this.getMethod = getMethod; }
/*     */     
/*     */     public void cleanup() {
/* 177 */       Closeables.closeQuietly(this.content);
/* 178 */       this.content = null;
/* 179 */       if (this.getMethod != null) {
/* 180 */         this.getMethod.releaseConnection();
/* 181 */         this.getMethod = null;
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   private static RemoteDataInfo retrieveRemoteData(String url, Folder.SyncData fsd) throws ServiceException, HttpException, IOException
/*     */   {
/* 188 */     assert (!Strings.isNullOrEmpty(url));
/*     */     
/* 190 */     HttpClient client = ZimbraHttpConnectionManager.getExternalHttpConnMgr().newHttpClient();
/* 191 */     HttpProxyUtil.configureProxy(client);
/*     */     
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/* 197 */     HttpMethodParams params = new HttpMethodParams();
/* 198 */     params.setParameter("http.protocol.content-charset", "utf-8");
/* 199 */     params.setSoTimeout(60000);
/*     */     
/* 201 */     GetMethod get = null;
/* 202 */     BufferedInputStream content = null;
/* 203 */     long lastModified = 0L;
/* 204 */     String expectedCharset = "utf-8";
/* 205 */     int redirects = 0;
/* 206 */     int statusCode = 404;
/*     */     try {
/*     */       do {
/* 209 */         String lcurl = url.toLowerCase();
/* 210 */         if (lcurl.startsWith("webcal:")) {
/* 211 */           url = "http:" + url.substring(7);
/* 212 */         } else if (lcurl.startsWith("feed:")) {
/* 213 */           url = "http:" + url.substring(5);
/* 214 */         } else if ((!lcurl.startsWith("http:")) && (!lcurl.startsWith("https:"))) {
/* 215 */           throw ServiceException.INVALID_REQUEST("url must begin with http: or https:", null);
/*     */         }
/*     */         
/*     */ 
/* 219 */         if (url.indexOf('@') != -1) {
/* 220 */           HttpURL httpurl = lcurl.startsWith("https:") ? new HttpsURL(url) : new HttpURL(url);
/* 221 */           if (httpurl.getUser() != null) {
/* 222 */             String user = httpurl.getUser();
/* 223 */             if (user.indexOf('%') != -1) {
/*     */               try {
/* 225 */                 user = URLDecoder.decode(user, "UTF-8");
/*     */               } catch (OutOfMemoryError e) {
/* 227 */                 Zimbra.halt("out of memory", e);
/*     */               } catch (Throwable t) {}
/*     */             }
/* 230 */             UsernamePasswordCredentials creds = new UsernamePasswordCredentials(user, httpurl.getPassword());
/* 231 */             client.getParams().setAuthenticationPreemptive(true);
/* 232 */             client.getState().setCredentials(AuthScope.ANY, creds);
/*     */           }
/*     */         }
/*     */         try
/*     */         {
/* 237 */           get = new GetMethod(url);
/*     */         } catch (OutOfMemoryError e) {
/* 239 */           Zimbra.halt("out of memory", e);return null;
/*     */         } catch (Throwable t) {
/* 241 */           throw ServiceException.INVALID_REQUEST("invalid url for feed: " + url, t);
/*     */         }
/* 243 */         get.setParams(params);
/* 244 */         get.setFollowRedirects(true);
/* 245 */         get.setDoAuthentication(true);
/* 246 */         get.addRequestHeader("User-Agent", HTTP_USER_AGENT);
/* 247 */         get.addRequestHeader("Accept", "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*");
/* 248 */         if ((fsd != null) && (fsd.getLastSyncDate() > 0L)) {
/* 249 */           String lastSyncAt = org.apache.commons.httpclient.util.DateUtil.formatDate(new Date(fsd.getLastSyncDate()));
/* 250 */           get.addRequestHeader("If-Modified-Since", lastSyncAt);
/*     */         }
/* 252 */         HttpClientUtil.executeMethod(client, get);
/*     */         
/* 254 */         Header locationHeader = get.getResponseHeader("location");
/* 255 */         if (locationHeader != null)
/*     */         {
/* 257 */           url = locationHeader.getValue();
/* 258 */           get.releaseConnection();
/*     */         } else {
/* 260 */           statusCode = get.getStatusCode();
/* 261 */           if (statusCode == 200) {
/* 262 */             content = new BufferedInputStream(get.getResponseBodyAsStream());
/* 263 */             expectedCharset = get.getResponseCharSet();
/*     */             
/* 265 */             Header lastModHdr = get.getResponseHeader("Last-Modified");
/* 266 */             if (lastModHdr == null) {
/* 267 */               lastModHdr = get.getResponseHeader("Date");
/*     */             }
/* 269 */             if (lastModHdr != null) {
/*     */               try {
/* 271 */                 Date d = org.apache.commons.httpclient.util.DateUtil.parseDate(lastModHdr.getValue());
/* 272 */                 lastModified = d.getTime();
/*     */               } catch (DateParseException e) {
/* 274 */                 ZimbraLog.misc.warn("unable to parse Last-Modified/Date header: " + lastModHdr.getValue(), e);
/* 275 */                 lastModified = System.currentTimeMillis();
/*     */               }
/*     */             } else {
/* 278 */               lastModified = System.currentTimeMillis();
/*     */             }
/* 280 */             break; } if (statusCode == 304) {
/* 281 */             ZimbraLog.misc.debug("Remote data at " + url + " not modified since last sync");
/* 282 */             return new RemoteDataInfo(statusCode, redirects, null, expectedCharset, lastModified);
/*     */           }
/* 284 */           throw ServiceException.RESOURCE_UNREACHABLE(get.getStatusLine().toString(), null, new ServiceException.Argument[0]);
/*     */         }
/*     */         
/*     */ 
/* 288 */         redirects++; } while (redirects <= 3);
/*     */     } catch (ServiceException ex) {
/* 290 */       if (get != null) {
/* 291 */         get.releaseConnection();
/*     */       }
/* 293 */       throw ex;
/*     */     } catch (HttpException ex) {
/* 295 */       if (get != null) {
/* 296 */         get.releaseConnection();
/*     */       }
/* 298 */       throw ex;
/*     */     } catch (IOException ex) {
/* 300 */       if (get != null) {
/* 301 */         get.releaseConnection();
/*     */       }
/* 303 */       throw ex;
/*     */     }
/* 305 */     RemoteDataInfo rdi = new RemoteDataInfo(statusCode, redirects, content, expectedCharset, lastModified);
/* 306 */     rdi.setGetMethod(get);
/* 307 */     return rdi;
/*     */   }
/*     */   
/*     */   @VisibleForTesting
/*     */   protected static SubscriptionData<?> retrieveRemoteDatasource(Account acct, RemoteDataInfo rdi, Folder.SyncData fsd) throws ServiceException, IOException
/*     */   {
/* 313 */     StringBuilder charset = new StringBuilder(rdi.expectedCharset);
/* 314 */     switch (getLeadingChar(rdi.content, charset)) {
/*     */     case -1: 
/* 316 */       throw ServiceException.PARSE_ERROR("empty body in response when fetching remote subscription", null);
/*     */     case 60: 
/* 318 */       return parseRssFeed(Element.parseXML(rdi.content), fsd, rdi.lastModified);
/*     */     case 66: case 98: 
/* 320 */       List<ZCalendar.ZVCalendar> icals = ZCalendar.ZCalendarBuilder.buildMulti(rdi.content, charset.toString());
/* 321 */       List<Invite> invites = Invite.createFromCalendar(acct, null, icals, true, true, null);
/*     */       
/* 323 */       for (Invite inv : invites) {
/* 324 */         if (inv.getUid() == null) {
/* 325 */           inv.setUid(LdapUtil.generateUUID());
/*     */         }
/*     */       }
/* 328 */       return new SubscriptionData(invites, rdi.lastModified);
/*     */     }
/* 330 */     throw ServiceException.PARSE_ERROR("unrecognized remote content", null);
/*     */   }
/*     */   
/*     */   public static SubscriptionData<?> retrieveRemoteDatasource(Account acct, String url, Folder.SyncData fsd)
/*     */     throws ServiceException
/*     */   {
/* 336 */     assert (!Strings.isNullOrEmpty(url));
/* 337 */     RemoteDataInfo rdi = null;
/*     */     try {
/* 339 */       rdi = retrieveRemoteData(url, fsd);
/*     */       SubscriptionData localSubscriptionData;
/* 341 */       if (rdi.statusCode == 304) {
/* 342 */         return SubscriptionData.NOT_MODIFIED();
/*     */       }
/* 344 */       if (rdi.redirects > 3) {
/* 345 */         throw ServiceException.TOO_MANY_PROXIES(url);
/*     */       }
/* 347 */       return retrieveRemoteDatasource(acct, rdi, fsd);
/*     */     } catch (HttpException e) {
/* 349 */       throw ServiceException.RESOURCE_UNREACHABLE("HttpException: " + e, e, new ServiceException.Argument[0]);
/*     */     } catch (IOException e) {
/* 351 */       throw ServiceException.RESOURCE_UNREACHABLE("IOException: " + e, e, new ServiceException.Argument[0]);
/*     */     } finally {
/* 353 */       if (rdi != null) {
/* 354 */         rdi.cleanup();
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   private static int getLeadingChar(BufferedInputStream is, StringBuilder charset) throws IOException {
/* 360 */     is.mark(128);
/*     */     
/* 362 */     int ch = is.read();
/* 363 */     switch (ch) {
/*     */     case 239: 
/* 365 */       if ((is.read() == 187) && (is.read() == 191)) {
/* 366 */         is.mark(128);
/* 367 */         ch = is.read();charset.setLength(0);charset.append("utf-8");
/*     */       }
/*     */       break;
/*     */     case 254: 
/* 371 */       if ((is.read() == 255) && (is.read() == 0)) {
/* 372 */         ch = is.read();charset.setLength(0);charset.append("utf-16");
/*     */       }
/*     */       break;
/*     */     case 255: 
/* 376 */       if (is.read() == 254) {
/* 377 */         ch = is.read();charset.setLength(0);charset.append("utf-16");
/*     */       }
/*     */       break;
/*     */     }
/*     */     
/* 382 */     for (int index = 0; (index < 120) && ((ch == 0) || (Character.isWhitespace(ch))); index++) {
/* 383 */       ch = is.read();
/*     */     }
/* 385 */     is.reset();
/* 386 */     return ch;
/*     */   }
/*     */   
/*     */   private static final class Enclosure {
/*     */     private final String url;
/*     */     private final String title;
/*     */     private final String cthdr;
/*     */     
/*     */     Enclosure(String url, String title, String ctype) {
/* 395 */       this.url = url;
/* 396 */       this.title = title;
/* 397 */       this.cthdr = ctype;
/*     */     }
/*     */     
/*     */     String getLocation() {
/* 401 */       return this.url;
/*     */     }
/*     */     
/*     */     String getDescription() {
/* 405 */       return this.title;
/*     */     }
/*     */     
/*     */     String getContentType() {
/* 409 */       ContentType ctype = new ContentType(this.cthdr == null ? "text/plain" : this.cthdr).cleanup();
/*     */       try {
/* 411 */         ctype.setParameter("name", FileUtil.trimFilename(URLDecoder.decode(this.url, "utf-8")));
/*     */       } catch (UnsupportedEncodingException e) {
/* 413 */         ctype.setParameter("name", FileUtil.trimFilename(this.url));
/*     */       }
/* 415 */       return ctype.toString();
/*     */     }
/*     */   }
/*     */   
/*     */   private static SubscriptionData<ParsedMessage> parseRssFeed(Element root, Folder.SyncData fsd, long lastModified) throws ServiceException
/*     */   {
/*     */     try {
/* 422 */       String rname = root.getName();
/* 423 */       if (rname.equals("feed")) {
/* 424 */         return parseAtomFeed(root, fsd, lastModified);
/*     */       }
/*     */       
/* 427 */       Element channel = root.getElement("channel");
/* 428 */       String hrefChannel = channel.getAttribute("link");
/* 429 */       String subjChannel = channel.getAttribute("title");
/* 430 */       InternetAddress addrChannel = new JavaMailInternetAddress("", subjChannel, "utf-8");
/* 431 */       Date dateChannel = com.zimbra.common.util.DateUtil.parseRFC2822Date(channel.getAttribute("lastBuildDate", null), new Date());
/*     */       
/* 433 */       List<Enclosure> enclosures = new ArrayList(3);
/* 434 */       SubscriptionData<ParsedMessage> sdata = new SubscriptionData();
/*     */       
/* 436 */       if (rname.equals("rss")) {
/* 437 */         root = channel;
/* 438 */       } else if (!rname.equals("RDF")) {
/* 439 */         throw ServiceException.PARSE_ERROR("unknown top-level rss element name: " + root.getQualifiedName(), null);
/*     */       }
/*     */       
/* 442 */       for (Element item : root.listElements("item"))
/*     */       {
/* 444 */         Date date = com.zimbra.common.util.DateUtil.parseRFC2822Date(item.getAttribute("pubDate", null), null);
/* 445 */         if (date == null) {
/* 446 */           date = com.zimbra.common.util.DateUtil.parseISO8601Date(item.getAttribute("date", null), dateChannel);
/*     */         }
/*     */         
/*     */ 
/* 450 */         InternetAddress addr = addrChannel;
/*     */         try {
/* 452 */           addr = parseAuthor(item.getAttribute("author"));
/*     */         } catch (Exception e) {
/* 454 */           addr = parseDublinCreator(stripXML(item.getAttribute("creator", null)), addr);
/*     */         }
/*     */         
/*     */ 
/* 458 */         String title = stripXML(item.getAttribute("title", subjChannel));
/* 459 */         String href = item.getAttribute("link", hrefChannel);
/* 460 */         String guid = item.getAttribute("guid", href);
/*     */         
/*     */ 
/* 463 */         if (fsd != null) { if (fsd.alreadySeen(guid == hrefChannel ? null : guid, date == dateChannel ? null : date)) {}
/*     */         }
/*     */         else
/*     */         {
/* 467 */           enclosures.clear();
/* 468 */           Element enc = item.getOptionalElement("enclosure");
/* 469 */           if (enc != null) {
/* 470 */             enclosures.add(new Enclosure(enc.getAttribute("url", null), null, enc.getAttribute("type", null)));
/*     */           }
/*     */           
/*     */ 
/* 474 */           String text = item.getAttribute("encoded", null);
/* 475 */           boolean html = text != null;
/* 476 */           if (text == null) {
/* 477 */             text = item.getAttribute("description", null);
/*     */           }
/* 479 */           if (text == null) {
/* 480 */             text = item.getAttribute("abstract", null);
/*     */           }
/* 482 */           if ((text == null) && (title != subjChannel)) {
/* 483 */             text = "";
/*     */           }
/* 485 */           if (text != null)
/*     */           {
/* 487 */             html |= ((text.indexOf("</") != -1) || (text.indexOf("/>") != -1) || (text.indexOf("<p>") != -1));
/*     */             
/* 489 */             ParsedMessage pm = generateMessage(title, text, href, html, addr, date, enclosures);
/* 490 */             sdata.recordItem(pm, guid, date.getTime());
/*     */           } } }
/* 492 */       sdata.recordFeedModifiedDate(lastModified);
/* 493 */       return sdata;
/*     */     } catch (UnsupportedEncodingException e) {
/* 495 */       throw ServiceException.FAILURE("error encoding rss channel name", e);
/*     */     }
/*     */   }
/*     */   
/*     */   private static SubscriptionData<ParsedMessage> parseAtomFeed(Element feed, Folder.SyncData fsd, long lastModified) throws ServiceException
/*     */   {
/*     */     try
/*     */     {
/* 503 */       InternetAddress addrFeed = parseAtomAuthor(feed.getOptionalElement("author"), null);
/* 504 */       if (addrFeed == null) {
/* 505 */         addrFeed = new JavaMailInternetAddress("", stripXML(feed.getAttribute("title")), "utf-8");
/*     */       }
/* 507 */       Date dateFeed = com.zimbra.common.util.DateUtil.parseISO8601Date(feed.getAttribute("updated", null), new Date());
/* 508 */       List<Enclosure> enclosures = new ArrayList();
/* 509 */       SubscriptionData<ParsedMessage> sdata = new SubscriptionData();
/*     */       
/* 511 */       for (Element item : feed.listElements("entry"))
/*     */       {
/* 513 */         Date date = com.zimbra.common.util.DateUtil.parseISO8601Date(item.getAttribute("updated", null), null);
/* 514 */         if (date == null) {
/* 515 */           date = com.zimbra.common.util.DateUtil.parseISO8601Date(item.getAttribute("modified", null), dateFeed);
/*     */         }
/*     */         
/*     */ 
/* 519 */         InternetAddress addr = parseAtomAuthor(item.getOptionalElement("author"), addrFeed);
/*     */         
/*     */ 
/* 522 */         Element tblock = item.getElement("title");
/* 523 */         String type = tblock.getAttribute("type", "text").trim().toLowerCase();
/* 524 */         String title = tblock.getText();
/* 525 */         if ((type.equals("html")) || (type.equals("xhtml")) || (type.equals("text/html")) || (type.equals("application/xhtml+xml"))) {
/* 526 */           title = stripXML(title);
/*     */         }
/*     */         
/*     */ 
/* 530 */         enclosures.clear();
/* 531 */         String href = "";
/* 532 */         for (Element link : item.listElements("link")) {
/* 533 */           String relation = link.getAttribute("rel", "alternate");
/* 534 */           if (relation.equals("alternate")) {
/* 535 */             href = link.getAttribute("href");
/* 536 */           } else if (relation.equals("enclosure")) {
/* 537 */             enclosures.add(new Enclosure(link.getAttribute("href", null), link.getAttribute("title", null), link.getAttribute("type", null)));
/*     */           }
/*     */         }
/* 540 */         String guid = item.getAttribute("id", href);
/*     */         
/*     */ 
/* 543 */         if (fsd != null) { if (fsd.alreadySeen((guid == null) || (guid.equals("")) ? null : guid, date == dateFeed ? null : date)) {}
/*     */         }
/*     */         else
/*     */         {
/* 547 */           Element content = item.getOptionalElement("content");
/* 548 */           if (content == null) {
/* 549 */             content = item.getOptionalElement("summary");
/*     */           }
/* 551 */           if (content != null)
/*     */           {
/*     */ 
/* 554 */             type = content.getAttribute("type", "text").trim().toLowerCase();
/* 555 */             boolean html = false;
/* 556 */             if ((type.equals("html")) || (type.equals("xhtml")) || (type.equals("text/html")) || (type.equals("application/xhtml+xml"))) {
/* 557 */               html = true;
/* 558 */             } else if ((!type.equals("text")) && (!type.equals("text/plain"))) {
/* 559 */               throw ServiceException.PARSE_ERROR("unsupported atom entry content type: " + type, null);
/*     */             }
/*     */             
/* 562 */             String text = content.getText();
/* 563 */             if (Strings.isNullOrEmpty(text)) {
/* 564 */               Element div = content.getElement("div");
/* 565 */               if (div != null)
/*     */               {
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/* 572 */                 text = div.getText();
/*     */               }
/*     */             }
/* 575 */             ParsedMessage pm = generateMessage(title, text, href, html, addr, date, enclosures);
/* 576 */             sdata.recordItem(pm, guid, date.getTime());
/*     */           } } }
/* 578 */       sdata.recordFeedModifiedDate(lastModified);
/* 579 */       return sdata;
/*     */     } catch (UnsupportedEncodingException e) {
/* 581 */       throw ServiceException.FAILURE("error encoding atom feed name", e);
/*     */     }
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   private static ParsedMessage generateMessage(String title, String text, String href, boolean html, InternetAddress addr, Date date, List<Enclosure> attach)
/*     */     throws ServiceException
/*     */   {
/* 592 */     String ctype = html ? "text/html; charset=\"utf-8\"" : "text/plain; charset=\"utf-8\"";
/* 593 */     StringBuilder content = new StringBuilder();
/* 594 */     if (html) {
/* 595 */       content.append("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML><HEAD><META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=utf-8\"></HEAD><BODY>").append(text).append("<p>").append(href).append("</p>").append("</BODY></HTML>");
/*     */     } else {
/* 597 */       content.append(text).append("\r\n\r\n").append(href);
/*     */     }
/* 599 */     return generateMessage(title, content.toString(), ctype, addr, date, attach);
/*     */   }
/*     */   
/*     */   private static ParsedMessage generateMessage(String title, String content, String ctype, InternetAddress addr, Date date, List<Enclosure> attach)
/*     */     throws ServiceException
/*     */   {
/*     */     Iterator<Enclosure> it;
/* 606 */     if (attach != null) {
/* 607 */       for (it = attach.iterator(); it.hasNext();) {
/* 608 */         if (((Enclosure)it.next()).getLocation() == null) {
/* 609 */           it.remove();
/*     */         }
/*     */       }
/*     */     }
/* 613 */     boolean hasAttachments = (attach != null) && (!attach.isEmpty());
/*     */     
/*     */ 
/* 616 */     if (title != null) {
/* 617 */       title = title.replaceAll("\\s+", " ");
/*     */     }
/*     */     
/*     */     try
/*     */     {
/* 622 */       MimeMessage mm = new Mime.FixedMimeMessage(JMSession.getSession());
/* 623 */       MimePart body = hasAttachments ? new ZMimeBodyPart() : mm;
/* 624 */       body.setText(content, "utf-8");
/* 625 */       body.setHeader("Content-Type", ctype);
/*     */       
/* 627 */       if (hasAttachments)
/*     */       {
/* 629 */         MimeMultipart mmp = new ZMimeMultipart("mixed");
/* 630 */         mmp.addBodyPart((BodyPart)body);
/* 631 */         for (Enclosure enc : attach) {
/* 632 */           MimeBodyPart part = new ZMimeBodyPart();
/* 633 */           part.setText("");
/* 634 */           part.addHeader("Content-Location", enc.getLocation());
/* 635 */           part.addHeader("Content-Type", enc.getContentType());
/* 636 */           if (enc.getDescription() != null) {
/* 637 */             part.addHeader("Content-Description", enc.getDescription());
/*     */           }
/* 639 */           part.addHeader("Content-Disposition", "attachment");
/* 640 */           mmp.addBodyPart(part);
/*     */         }
/* 642 */         mm.setContent(mmp);
/*     */       }
/*     */       
/* 645 */       mm.setSentDate(date);
/* 646 */       mm.addFrom(new InternetAddress[] { addr });
/* 647 */       mm.setSubject(title, "utf-8");
/*     */       
/* 649 */       mm.saveChanges();
/* 650 */       return new ParsedMessage(mm, date.getTime(), false);
/*     */     } catch (MessagingException e) {
/* 652 */       throw ServiceException.PARSE_ERROR("error wrapping feed item in MimeMessage", e);
/*     */     }
/*     */   }
/*     */   
/*     */   private static InternetAddress parseDublinCreator(String creator, InternetAddress addrChannel) {
/* 657 */     if ((creator == null) || (creator.equals(""))) {
/* 658 */       return addrChannel;
/*     */     }
/*     */     
/*     */ 
/* 662 */     String lc = creator.trim().toLowerCase();String address = "";String personal = creator;
/* 663 */     int mailto = lc.indexOf("mailto:");
/* 664 */     if ((mailto == 0) && (lc.length() <= 7))
/* 665 */       return addrChannel;
/* 666 */     if (mailto == 0) {
/* 667 */       personal = null;
/* 668 */       address = creator = creator.substring(7);
/* 669 */     } else if (mailto != -1)
/*     */     {
/* 671 */       char delimit = creator.charAt(mailto - 1);char complement = '\000';
/* 672 */       if (delimit == '[') {
/* 673 */         complement = ']';
/* 674 */       } else if (delimit == '(') {
/* 675 */         complement = ')';
/*     */       }
/* 677 */       int closing = creator.indexOf(complement, mailto + 7);
/* 678 */       if ((closing != -1) && (closing != mailto + 7)) {
/* 679 */         address = creator.substring(mailto + 7, closing);
/* 680 */         personal = (creator.substring(0, mailto - 1) + creator.substring(closing + 1)).trim();
/*     */       }
/*     */     }
/*     */     try
/*     */     {
/* 685 */       return new JavaMailInternetAddress(address, personal, "utf-8");
/*     */     } catch (UnsupportedEncodingException e) {
/*     */       try {
/* 688 */         return new JavaMailInternetAddress("", creator, "utf-8");
/*     */       } catch (UnsupportedEncodingException e) {} }
/* 690 */     return addrChannel;
/*     */   }
/*     */   
/*     */   private static InternetAddress parseAuthor(String author) throws IOException, ParseException {
/* 694 */     if ((author != null) && (author.indexOf('@') == -1)) {
/* 695 */       return new JavaMailInternetAddress("", stripXML(author), "utf-8");
/*     */     }
/* 697 */     return new JavaMailInternetAddress(author);
/*     */   }
/*     */   
/*     */   private static InternetAddress parseAtomAuthor(Element author, InternetAddress addrChannel)
/*     */   {
/* 702 */     if (author == null) {
/* 703 */       return addrChannel;
/*     */     }
/*     */     
/* 706 */     String address = stripXML(author.getAttribute("email", ""));
/* 707 */     String personal = stripXML(author.getAttribute("name", ""));
/* 708 */     if ((personal.equals("")) && (address.equals(""))) {
/* 709 */       return addrChannel;
/*     */     }
/*     */     try
/*     */     {
/* 713 */       return new JavaMailInternetAddress(address, personal, "utf-8");
/*     */     } catch (UnsupportedEncodingException e) {
/*     */       try {
/* 716 */         return new JavaMailInternetAddress("", address + personal, "utf-8");
/*     */       } catch (UnsupportedEncodingException e) {} }
/* 718 */     return addrChannel;
/*     */   }
/*     */   
/*     */   private static class UnescapedContent extends DefaultHandler {
/* 722 */     private final StringBuilder str = new StringBuilder();
/*     */     
/*     */ 
/*     */ 
/*     */     public void startDocument()
/*     */     {
/* 728 */       this.str.setLength(0);
/*     */     }
/*     */     
/*     */     public void characters(char[] ch, int offset, int length)
/*     */     {
/* 733 */       this.str.append(ch, offset, length);
/*     */     }
/*     */     
/*     */     public void startElement(String uri, String localName, String qName, Attributes attributes)
/*     */     {
/* 738 */       if (this.str.length() > 0) {
/* 739 */         String name = localName.toUpperCase();
/* 740 */         if (((name.equals("P")) || (name.equals("BR")) || (name.equals("HR"))) && 
/* 741 */           (!Character.isWhitespace(this.str.charAt(this.str.length() - 1)))) {
/* 742 */           this.str.append(" ");
/*     */         }
/*     */       }
/*     */     }
/*     */     
/*     */ 
/*     */     public String toString()
/*     */     {
/* 750 */       return this.str.toString();
/*     */     }
/*     */   }
/*     */   
/*     */   @VisibleForTesting
/*     */   static final String stripXML(String title) {
/* 756 */     if (title == null)
/* 757 */       return "";
/* 758 */     if ((title.indexOf('<') == -1) && (title.indexOf('&') == -1)) {
/* 759 */       return title;
/*     */     }
/*     */     
/* 762 */     XMLReader parser = new SAXParser();
/* 763 */     ContentHandler handler = new UnescapedContent();
/* 764 */     parser.setContentHandler(handler);
/*     */     try {
/* 766 */       parser.parse(new InputSource(new StringReader(title)));
/* 767 */       return handler.toString();
/*     */     } catch (Exception e) {}
/* 769 */     return title;
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/service/FeedManager.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */